/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    soft_i2c_simple.c
  * @brief   Simplified Software I2C driver implementation
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include <stdint.h>

/* USER CODE BEGIN 0 */

// I2C时序延时函数 - 微秒级延时
static void I2C_Delay(void)
{
    // 简单的循环延时，约1-2微秒
    for(volatile uint8_t i = 0; i < 10; i++);
}

/**
  * @brief  Initialize software I2C
  * @retval None
  */
void Soft_I2C_Init(void)
{
    // Set SDA and SCL as output high
    HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_SET);
}

/**
  * @brief  Generate I2C start condition
  * @retval None
  */
void Soft_I2C_Start(void)
{
    // SDA high, SCL high
    HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_SET);
    I2C_Delay();
    
    // SDA low while SCL high
    HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_RESET);
    I2C_Delay();
    
    // SCL low
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_RESET);
    I2C_Delay();
}

/**
  * @brief  Generate I2C stop condition
  * @retval None
  */
void Soft_I2C_Stop(void)
{
    // SDA low, SCL low
    HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_RESET);
    I2C_Delay();
    
    // SCL high while SDA low
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_SET);
    I2C_Delay();
    
    // SDA high while SCL high
    HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_SET);
    I2C_Delay();
}

/**
  * @brief  Send ACK signal
  * @retval None
  */
void Soft_I2C_Ack(void)
{
    // SDA low
    HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_RESET);
    I2C_Delay();
    
    // SCL high
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_SET);
    I2C_Delay();
    
    // SCL low
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_RESET);
    I2C_Delay();
}

/**
  * @brief  Send NACK signal
  * @retval None
  */
void Soft_I2C_NAck(void)
{
    // SDA high
    HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_SET);
    I2C_Delay();
    
    // SCL high
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_SET);
    I2C_Delay();
    
    // SCL low
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_RESET);
    I2C_Delay();
}

/**
  * @brief  Wait for ACK signal
  * @retval 0: ACK received, 1: NACK received
  */
uint8_t Soft_I2C_WaitAck(void)
{
    uint8_t ack = 0;
    
    // Set SDA as input
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = TM1650_SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(TM1650_SDA_GPIO_Port, &GPIO_InitStruct);
    
    // SCL high
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_SET);
    I2C_Delay();
    
    // Read SDA
    if (HAL_GPIO_ReadPin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin) == GPIO_PIN_SET)
    {
        ack = 1; // NACK
    }
    else
    {
        ack = 0; // ACK
    }
    
    // SCL low
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_RESET);
    I2C_Delay();
    
    // Set SDA back as output
    GPIO_InitStruct.Pin = TM1650_SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(TM1650_SDA_GPIO_Port, &GPIO_InitStruct);
    
    return ack;
}

/**
  * @brief  Send one byte via I2C
  * @param  data: Byte to send
  * @retval None
  */
void Soft_I2C_SendByte(uint8_t data)
{
    uint8_t i;
    
    for (i = 0; i < 8; i++)
    {
        // SCL low
        HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_RESET);
        I2C_Delay();
        
        // Set SDA according to bit
        if (data & 0x80)
        {
            HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_SET);
        }
        else
        {
            HAL_GPIO_WritePin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin, GPIO_PIN_RESET);
        }
        I2C_Delay();
        
        // SCL high
        HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_SET);
        I2C_Delay();
        
        data <<= 1;
    }
    
    // SCL low
    HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_RESET);
    I2C_Delay();
    
    // 等待ACK确认
    Soft_I2C_WaitAck();
}

/**
  * @brief  Read one byte via I2C
  * @retval Received byte
  */
uint8_t Soft_I2C_ReadByte(void)
{
    uint8_t i, data = 0;
    
    // Set SDA as input
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = TM1650_SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(TM1650_SDA_GPIO_Port, &GPIO_InitStruct);
    
    for (i = 0; i < 8; i++)
    {
        data <<= 1;
        
        // SCL high
        HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_SET);
        I2C_Delay();
        
        // Read SDA
        if (HAL_GPIO_ReadPin(TM1650_SDA_GPIO_Port, TM1650_SDA_Pin) == GPIO_PIN_SET)
        {
            data |= 0x01;
        }
        
        // SCL low
        HAL_GPIO_WritePin(TM1650_CLK_GPIO_Port, TM1650_CLK_Pin, GPIO_PIN_RESET);
        I2C_Delay();
    }
    
    // Set SDA back as output
    GPIO_InitStruct.Pin = TM1650_SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(TM1650_SDA_GPIO_Port, &GPIO_InitStruct);
    
    return data;
}

/* USER CODE END 0 */
